/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package hellfire.system.graphics;

import hellfire.system.engine.Main;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;

public class GLSpriteAtlas implements GLDrawable
{
	
	//Shader program ID
	private final int program;
    
	//Image resource ID
    private int resourceId;
    
    //Position & Size properties
    private GLVec2 pos, dim;
    
    //Mask color
    private float[] maskColor; //not GLColor to avoid 'new' when feeding shader
    
    //Rotation (clockwise, in degrees)
    private float rotation;
    
    //Atlas dimensions
    private GLVec2 atlasDims;
    
    //Sprite Id
    private int spriteId;
    
    //Cache for handles
    private GLCache cache = new GLCache();
    
    public GLSpriteAtlas(int resource, GLVec2 pos, GLVec2 dim, GLVec2 atlasDims) 
    {
    	program = Main.UI.renderer.getShaderProgram(Shaders.pAtlas);
    	this.pos = pos;
    	this.dim = dim;
    	this.resourceId = resource;
    	this.maskColor = GLColor.WHITE.asFloatArray();
    	this.rotation = 0;
    	this.atlasDims = atlasDims;
    	this.spriteId = 0;
    	if(atlasDims.x() < 1 || atlasDims.y() < 1)
    		this.atlasDims = new GLVec2(1,1);
    }
    
    public void setMaskColor(GLColor c)
    {
    	if(c == null) 
    		this.maskColor = GLColor.WHITE.asFloatArray();
    	else this.maskColor = c.asFloatArray();
    }
    
    /**
     * @return A new GLColor object. It is unique.
     */
    public GLColor getMaskColor()
    {
    	return new GLColor(this.maskColor);
    }
    
    public void setSpriteId(int id)
    {
    	id %= (int)(atlasDims.x() * atlasDims.y());
    	this.spriteId = id;
    }
    
    public int getSpriteId()
    {
    	return this.spriteId;
    }
    
    public void setRotation(float rot)
    {
    	this.rotation = rot;
    }
    
    public float getRotation()
    {
    	return this.rotation;
    }
    
    public void setImageResource(int resource, GLVec2 atlasDims)
    {
    	this.resourceId = resource;
    	this.atlasDims = atlasDims;
    	if(atlasDims.x() < 1 || atlasDims.y() < 1)
    		this.atlasDims = new GLVec2(1,1);
    	cache.set(0, Main.UI.renderer.ResourceIdToTexture(resourceId));
    }
    
    public int getImageResource()
    {
    	return this.resourceId;
    }
    
    public GLVec2 getAtlasDims()
    {
    	return this.atlasDims;
    }
    
    public void draw(float[] matrix) 
    {
    	int mPositionHandle;
    	int mTexelHandle;
        int mColorHandle;
        int mMVPMatrixHandle;
        int mTextureHandle;
        int mTexture;
        int mSpriteIdHandle;
        int mAtlasDimsHandle;
        
        Main.UI.renderer.pushMatrix(matrix);
        Matrix.translateM(matrix, 0, pos.x(), pos.y(), 0);
        Matrix.rotateM(matrix, 0, rotation, 0, 0, 1);
    	Matrix.scaleM(matrix, 0, matrix, 0, dim.x(), dim.y(), 1);
        GLES20.glUseProgram(program);
    	
        if((mTexture = cache.get(0)) < 0)
        	mTexture = cache.set(0, Main.UI.renderer.ResourceIdToTexture(resourceId));
    	
        if((mPositionHandle = cache.get(1)) < 0)
        	mPositionHandle = cache.set(1, GLES20.glGetAttribLocation(program, "aPosition"));
        
        if((mColorHandle = cache.get(2)) < 0)
        	mColorHandle = cache.set(2, GLES20.glGetUniformLocation(program, "uColor"));
        
        if((mTexelHandle = cache.get(3)) < 0)
        	mTexelHandle = cache.set(3, GLES20.glGetAttribLocation(program, "aTexCoord"));
        
        if((mTextureHandle = cache.get(4)) < 0)
        	mTextureHandle = cache.set(4, GLES20.glGetUniformLocation(program, "uTexture"));
        
        if((mMVPMatrixHandle = cache.get(5)) < 0) 
        	mMVPMatrixHandle = cache.set(5, GLES20.glGetUniformLocation(program, "uMVPMatrix"));
        
        if((mSpriteIdHandle = cache.get(6)) < 0) 
        	mSpriteIdHandle = cache.set(6, GLES20.glGetUniformLocation(program, "uSpriteId"));
        
        if((mAtlasDimsHandle = cache.get(7)) < 0) 
        	mAtlasDimsHandle = cache.set(7, GLES20.glGetUniformLocation(program, "uAtlasDims"));
        
      
        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(
                mPositionHandle, 3,
                GLES20.GL_FLOAT, false,
                12, GLRect.vertexBuffer);
        
        GLES20.glEnableVertexAttribArray(mTexelHandle);
        GLES20.glVertexAttribPointer(
        		mTexelHandle, 2,
                GLES20.GL_FLOAT, false,
                0, GLImage.texCoordBuffer);
        
        GLES20.glUniform4fv(mColorHandle, 1, maskColor, 0);
        
        GLES20.glUniform1i(mSpriteIdHandle, spriteId);
        
        int[] arr = new int[]{(int) atlasDims.x(), (int) atlasDims.y()};
        GLES20.glUniform2iv(mAtlasDimsHandle, 0, arr, 0);
        
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture);
        GLES20.glUniform1i(mTextureHandle, 0);
        
         // Apply the projection and view transformation
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, matrix, 0);
        //GLRenderer.checkGLError("glUniformMatrix4fv");

        // Draw the square
        GLES20.glDrawElements(
                GLES20.GL_TRIANGLES, GLRect.drawOrder.length,
                GLES20.GL_UNSIGNED_SHORT, GLRect.drawListBuffer);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mTexelHandle);
        Main.UI.renderer.popMatrix(matrix);
    }
    
    public boolean checkBounds(GLVec2 pos)
    {
    	return (pos.x() > this.pos.x() - this.dim.x()
    			&& pos.x() < this.pos.x() + this.dim.x()
    			&& pos.y() < this.pos.y() + this.dim.y()
    			&& pos.y() > this.pos.y() - this.dim.y());
    }
    
    public GLVec2 getPos() 
	{
		return pos;
	}
    
    public void setPos(GLVec2 pos) 
	{
		this.pos = pos;
	}
}